package services

import (
	"bytes"
	"context"
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/prometheus/common/model"
	"github.com/spf13/viper"
	"github.com/uber/tchannel-go"
	"github.com/uber/tchannel-go/thrift"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/request"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/pkg/dingding"
	promentheus "go.didapinche.com/foundation/plat-echo/pkg/prometheus"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/uic_service_api"
	"go.didapinche.com/time"
	"go.uber.org/zap"
	"net/url"
	"strconv"
	"text/template"
	time2 "time"
)

type AlertHistoryService interface {
	FindStatHistory(ctx context.Context, appID uint64, metricType string, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error)
	FindByGraphID(ctx context.Context, id uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error)
	FindAlertCountByAppId(ctx context.Context, appID uint64, start, end time.Time) ([]*map[string]interface{}, error)
	FindAlertRecord(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, error)
	FindAlertRecordByAppID(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error)
	FindAlertHistoryByApiID(ctx context.Context, req *request.AlertRecordReq) ([]*models.AlertHistory, int64, error)
	FindAlertRecordByDashboard(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error)
	FindAlertRecordByApiID(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error)
	DeleteByRepeat(ctx context.Context) error
	Claim(ctx context.Context, alerts []*request.Alert, userID, userName string) error
	Find(ctx context.Context, req *request.AlertHistoryReq) ([]*response.AlertRecordResp, int64, error)
}

type alertHistoryService struct {
	appRepository       repositories.ApplicationRepository
	apiRepository       repositories.ApiRepository
	graphRepository     repositories.GraphRepository
	hostRepository      repositories.HostRepository
	dingding            dingding.DingDingService
	dashboardRepository repositories.DashboardRepository
	uic                 uic_service_api.TChanUicService
	repository          repositories.AlertHistoryRepository
	currentRepository   repositories.AlertCurrentRepository
	templateRepository  repositories.TemplateRepository
	prome               *promentheus.PrometheusService
	logger              *zap.Logger
}

func NewAlertHistoryService(repository repositories.AlertHistoryRepository,
	currentRepository repositories.AlertCurrentRepository,
	templateRepository repositories.TemplateRepository,
	appRespository repositories.ApplicationRepository,
	apiRepository repositories.ApiRepository,
	dingding dingding.DingDingService,
	prome *promentheus.PrometheusService,
	uic uic_service_api.TChanUicService,
	graphRepository repositories.GraphRepository,
	hostRepository repositories.HostRepository,
	dashboardRepository repositories.DashboardRepository,
	logger *zap.Logger) AlertHistoryService {
	return &alertHistoryService{
		repository:          repository,
		graphRepository:     graphRepository,
		apiRepository:       apiRepository,
		appRepository:       appRespository,
		hostRepository:      hostRepository,
		dashboardRepository: dashboardRepository,
		dingding:            dingding,
		prome:               prome,
		uic:                 uic,
		logger:              logger,
		templateRepository:  templateRepository,
		currentRepository:   currentRepository,
	}
}

func (s alertHistoryService) FindStatHistory(ctx context.Context, appID uint64, metricType string, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error) {
	templateID, err := strconv.ParseUint(metricType, 10, 64)
	if err != nil {
		return nil, 0, errors.Wrap(err, "strconv.ParseUint error")
	}
	historys, count, err := s.repository.FindStatHistory(ctx, appID, templateID, start, end, pageNum, pageSize)
	if err != nil {
		s.logger.Error("call AlertHistoryRepository FindStatHistory error")
		return nil, 0, errors.Wrap(err, "call AlertHistoryRepository FindStatHistory error")
	}
	return historys, count, nil
}

func (s alertHistoryService) FindByGraphID(ctx context.Context, id uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error) {

	historys, count, err := s.repository.FindByGraphID(ctx, id, start, end, pageNum, pageSize)
	if err != nil {
		s.logger.Error("call AlertHistoryRepository FindByGraphID error")
		return nil, 0, errors.Wrap(err, "call AlertHistoryRepository FindByGraphID error")
	}
	return historys, count, nil
}

func (s alertHistoryService) FindAlertCountByAppId(ctx context.Context, appID uint64, start, end time.Time) ([]*map[string]interface{}, error) {
	records, err := s.repository.FindAlertCountByAppId(ctx, appID, start, end)
	if err != nil {
		s.logger.Error("call FindAlertCountByAppId FindByGraphID error")
		return nil, errors.Wrap(err, "call FindAlertCountByAppId FindByGraphID error")
	}
	stat := make(map[string]int, 0)
	for _, record := range records {
		date := data.FormatDate(record.StartTime.Time, data.Format_yyyyMMddHH)
		if _, ok := stat[date]; !ok {
			stat[date] = 0
		}
		stat[date] = stat[date] + 1
	}
	return fillCountData(start.Time, end.Time, stat), nil
}

func fillCountData(start time2.Time, end time2.Time, stat map[string]int) []*map[string]interface{} {
	result := make([]*map[string]interface{}, 0)
	end = end.Add(10 * time2.Minute)
	temp := start
	for temp.Before(end) {
		date := data.FormatDate(temp, data.Format_yyyyMMddHH)
		result = append(result, &map[string]interface{}{
			"time": data.FormatDate(temp, data.FormatLayout),
			"data": stat[date],
		})
		temp = temp.Add(time2.Hour)
	}

	return result
}

func (s alertHistoryService) FindAlertRecord(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, error) {
	alerts, count, err := s.repository.FindByGraphID(ctx, req.GraphID, req.StartTime, req.EndTime, req.PageNum, req.PageSize/2)
	if err != nil {
		s.logger.Error("call repository FindByGraphID error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call repository FindByGraphID error")
	}
	alerts2 := response.MakeAlertRecordResps(alerts)
	return alerts2, count * 2, nil
}

func (s alertHistoryService) FindAlertRecordByAppID(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error) {
	currentAlert, err := s.currentRepository.FindByAppID(ctx, int(req.AppID), req.StartTime, req.EndTime)
	if err != nil {
		s.logger.Error("call currentRepository FindByAppID error", zap.Error(err))
		return nil, 0, 0, errors.Wrap(err, "call currentRepository FindByAppID error")
	}
	alerts, count, err := s.repository.FindByDateAndID(ctx, req.AppID, 0, req.StartTime, req.EndTime, req.PageNum, req.PageSize)
	if err != nil {
		s.logger.Error("call repository FindByDateAndID error", zap.Error(err))
		return nil, 0, 0, errors.Wrap(err, "call repository FindByDateAndID error")
	}
	alerts2 := response.MakeCurrentAlertRecordResps(currentAlert)
	alerts2 = append(alerts2, response.MakeAlertRecordResps(alerts)...)
	return alerts2, count, int64(len(currentAlert)), nil
}

func (s alertHistoryService) FindAlertHistoryByApiID(ctx context.Context, req *request.AlertRecordReq) ([]*models.AlertHistory, int64, error) {
	//currentAlert, err := s.currentRepository.FindByApiID(ctx, int(req.AppID), req.StartTime, req.EndTime)
	//if err != nil {
	//	s.logger.Error("call currentRepository FindByApiID error", zap.Error(err))
	//	return nil,  0, errors.Wrap(err, "call currentRepository FindByApiID error")
	//}
	alerts, count, err := s.repository.FindByDateAndID(ctx, req.ApiID, 1, req.StartTime, req.EndTime, req.PageNum, req.PageSize)
	if err != nil {
		s.logger.Error("call repository FindByDateAndID error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call repository FindByDateAndID error")
	}

	return alerts, count, nil
}

func (s alertHistoryService) FindAlertRecordByDashboard(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error) {
	currentAlert, err := s.currentRepository.FindByDashboardID(ctx, int(req.AppID), req.StartTime, req.EndTime)
	if err != nil {
		s.logger.Error("call currentRepository FindByDashboardID error", zap.Error(err))
		return nil, 0, 0, errors.Wrap(err, "call currentRepository FindByDashboardID error")
	}
	alerts, count, err := s.repository.FindByDateAndID(ctx, req.DashboardID, 3, req.StartTime, req.EndTime, req.PageNum, req.PageSize)
	if err != nil {
		s.logger.Error("call repository FindByDateAndID error", zap.Error(err))
		return nil, 0, 0, errors.Wrap(err, "call repository FindByDateAndID error")
	}
	alerts2 := response.MakeCurrentAlertRecordResps(currentAlert)
	alerts2 = append(alerts2, response.MakeAlertRecordResps(alerts)...)
	return alerts2, count, int64(len(currentAlert)), nil
}

func (s alertHistoryService) FindAlertRecordByApiID(ctx context.Context, req *request.AlertRecordReq) ([]*response.AlertRecordResp, int64, int64, error) {
	if req.GraphID != 0 {
		currentAlert, err := s.currentRepository.FindByGraphID(ctx, req.GraphID, req.StartTime, req.EndTime)
		if err != nil {
			s.logger.Error("call currentRepository FindByGraphID error", zap.Error(err))
			return nil, 0, 0, errors.Wrap(err, "call currentRepository FindAlertRecordByApiID error")
		}
		alerts, count, err := s.repository.FindByGraphID(ctx, req.GraphID, req.StartTime, req.EndTime, req.PageNum, req.PageSize)
		if err != nil {
			s.logger.Error("call repository FindByGraphID error", zap.Error(err))
			return nil, 0, 0, errors.Wrap(err, "call repository FindByDateAndID error")
		}
		alerts2 := response.MakeCurrentAlertRecordResps(currentAlert)
		alerts2 = append(alerts2, response.MakeAlertRecordResps(alerts)...)
		return alerts2, count, int64(len(currentAlert)), nil
	} else if req.ApiID != 0 {
		currentAlert, err := s.currentRepository.FindByApiID(ctx, int(req.ApiID), req.StartTime, req.EndTime)
		if err != nil {
			s.logger.Error("call currentRepository FindAlertRecordByApiID error", zap.Error(err))
			return nil, 0, 0, errors.Wrap(err, "call currentRepository FindAlertRecordByApiID error")
		}
		alerts, count, err := s.repository.FindByDateAndID(ctx, req.ApiID, 1, req.StartTime, req.EndTime, req.PageNum, req.PageSize)
		if err != nil {
			s.logger.Error("call repository FindByDateAndID error", zap.Error(err))
			return nil, 0, 0, errors.Wrap(err, "call repository FindByDateAndID error")
		}
		alerts2 := response.MakeCurrentAlertRecordResps(currentAlert)
		alerts2 = append(alerts2, response.MakeAlertRecordResps(alerts)...)
		return alerts2, count, int64(len(currentAlert)), nil
	}
	return nil, 0, 0, nil
}
func (s alertHistoryService) DeleteByRepeat(ctx context.Context) error {
	historys, err := s.repository.FindByRepeat(ctx)
	if err != nil {
		return err
	}
	ids := make([]uint64, 0)
	for _, h := range historys {
		ids = append(ids, h.ID)
		if len(ids) == 100 {
			if err := s.repository.Delete(ctx, ids); err != nil {
				return err
			}
			ids = make([]uint64, 0)
		}
	}

	if err := s.repository.Delete(ctx, ids); err != nil {
		return err
	}
	return nil
}
func (s alertHistoryService) Find(ctx context.Context, req *request.AlertHistoryReq) ([]*response.AlertRecordResp, int64, error) {
	if req.ApiID == 0 && req.HostID == 0 && req.DashboardID == 0 && req.AppID == 0 && req.GraphID == 0 && req.Query != "" {
		apps, err := s.appRepository.Find(ctx, req.Query, 0, nil)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepository.Find error")
		}
		for i, app := range apps {
			req.AppIDs = append(req.AppIDs, app.ID)
			if i == 10 {
				break
			}
		}
		apis, _, err := s.apiRepository.FindByQuery(ctx, req.Query, 1, 10)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepository.Find error")
		}
		for _, api := range apis {
			req.ApiIDs = append(req.ApiIDs, api.ID)
		}
		hosts, _, err := s.hostRepository.FindByEnvIDAndGroupAndDCAndNameAndLikeName(ctx, 0, "", "", "", req.Query, 1, 10)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepository.Find error")
		}
		for _, host := range hosts {
			req.HostIDs = append(req.HostIDs, host.ID)
		}
		dashboards, _, err := s.dashboardRepository.FindByName(ctx, req.Query, 1, 10)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepository.Find error")
		}
		for _, dashboard := range dashboards {
			req.DashboardIDs = append(req.DashboardIDs, dashboard.ID)
		}
	}
	if req.DashboardID != 0 {
		dashboards, err := s.dashboardRepository.FindByParentID(ctx, req.DashboardID)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call dashboardRepository.FindByParentID error")
		}
		for _, d := range dashboards {
			req.DashboardIDs = append(req.DashboardIDs, d.ID)
		}
		req.DashboardIDs = append(req.DashboardIDs, req.DashboardID)
	}
	alert := make([]*response.AlertRecordResp, 0)
	if req.AlertStatus == -1 {
		currents, err := s.currentRepository.Find(ctx, req)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call currentRepository.Find error")
		}
		history, err := s.repository.Find(ctx, req)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call historyRepository.Find error")
		}
		alert = append(alert, response.MakeCurrentAlertRecordResps(currents)...)
		alert = append(alert, response.MakeAlertRecordResps2(history)...)
	} else if req.AlertStatus == 0 {
		currents, err := s.currentRepository.Find(ctx, req)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call currentRepository.Find error")
		}
		alert = append(alert, response.MakeCurrentAlertRecordResps(currents)...)
	} else {
		history, err := s.repository.Find(ctx, req)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call historyRepository.Find error")
		}
		alert = append(alert, response.MakeAlertRecordResps2(history)...)
	}
	start := (req.PageNum - 1) * req.PageSize
	end := req.PageNum * req.PageSize
	if end > len(alert) {
		end = len(alert)
	}
	if start > len(alert) {
		req.PageNum = 0
	}
	alert2 := alert[start:end]
	for _, a := range alert2 {
		if a.ApiID != 0 {
			api, err := s.apiRepository.FindByID(ctx, a.ApiID)
			if err != nil {
				return nil, 0, err
			}
			if api.Type == 0 {
				api.TemplateType = 1
			} else {
				api.TemplateType = 2
			}
			a.ApiInfo = api
			if a.AppID != 0 {
				app, err := s.appRepository.FindByID(ctx, a.AppID)
				if err != nil {
					return nil, 0, err
				}
				a.AppInfo = app
			}
		} else if a.AppID != 0 {
			app, err := s.appRepository.FindByID(ctx, a.AppID)
			if err != nil {
				return nil, 0, err
			}
			app.Owners = app.GetOwners()
			a.AppInfo = app
		} else if a.HostID != 0 {
			host, err := s.hostRepository.FindByID(ctx, a.ApiID)
			if err != nil {
				return nil, 0, err
			}
			a.HostInfo = host
		} else if a.DashboardID != 0 {
			dashboard, err := s.dashboardRepository.FindByID(ctx, a.DashboardID)
			if err != nil {
				return nil, 0, err
			}
			a.DashboardInfo = dashboard
		}
	}
	return alert2, int64(len(alert)), nil
}
func (s alertHistoryService) Claim(ctx context.Context, alerts []*request.Alert, userID, userName string) error {
	ids1 := make([]uint64, 0)
	ids2 := make([]uint64, 0)
	for _, a := range alerts {
		if a.Status == 0 {
			ids1 = append(ids1, a.ID)
		} else {
			ids2 = append(ids2, a.ID)
		}
	}
	user := new(models.ApplicationOwner)
	user.UserName = userName
	user.UserID = userID
	user2, _ := json.Marshal(user)
	if len(ids2) > 0 {
		if err := s.repository.UpdateByID(ctx, ids2, string(user2)); err != nil {
			return errors.Wrap(err, "call alertHistoryHistory UpdateByID error")
		}
	}
	if len(ids1) > 0 {
		if err := s.currentRepository.UpdateByID(ctx, ids1, string(user2)); err != nil {
			return errors.Wrap(err, "call alertHistoryHistory UpdateByID error")
		}
	}
	go func() {
		ctx2 := context.Background()
		for _, id := range ids1 {
			current, err := s.currentRepository.FindByID(ctx2, id)
			if err == nil {
				s.SendDingding2(ctx2, current)
			}
		}
		for _, id := range ids2 {
			history, err := s.repository.FindByID(ctx2, id)
			if err == nil {
				s.SendDingding(ctx2, history)
			}
		}
	}()

	return nil
}

const AlertClaimTmp = `
{{$owners := .owners}}
{{$upstreams := .upstreams}}
#### 认领 *{{ .name }}*
---
{{ if ne .app "" }}##### 告警信息：*{{ .message }}* {{end}}
{{ if ne .app "" }}##### 应用：*{{ .app }}* {{end}}
{{ if ne .api "" }}##### 接口：*{{ .api }}* {{end}}
{{ if ne .host "" }}##### 主机：*{{ .host }}* {{end}}
{{ if ne .alert "" }}##### 告警级别：*{{ .alert }}* {{end}}
{{ if ne .claim "" }}##### 认领人：*{{ .claim }}* {{end}}
##### 阈值：*{{ .threshold }}*
##### Value：*{{ .value }}*
{{range $_,$owner := $owners}}[@{{$owner}}](){{end}}
>  [查看详情]({{ .urlDetail }}) 
---
###### {{ .expression }}
{{ if ne .hasUp "" }}
---
###### 上游服务
{{range $_,$stream := $upstreams}}
{{$stream.Name}} {{range $_,$owner := $stream.Users}}[@{{$owner}}](){{end}}
{{end}}    
{{end}}`

func (s alertHistoryService) SendDingding(ctx context.Context, history *models.AlertHistory) error {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
		}
	}()
	var token string
	s.logger.Debug("钉钉告警开始获取graphID")
	t, err := template.New("temp").Parse(AlertClaimTmp)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	temMap := map[string]interface{}{
		"api":        "",
		"app":        "",
		"message":    "",
		"host":       "",
		"hasUp":      "",
		"alert":      "",
		"value":      "",
		"expression": "",
		"claim":      "",
		"upstreams":  "",
		"threshold":  "",
	}
	//查询dingding token
	s.logger.Debug("钉钉告警开始获取app")
	if len((*history.Alerts)) == 0 {
		return errors.New("don't exists alert message")
	}
	//上游owner
	upsteamUsers := make([]string, 0)
	info := (*history.Alerts)[0]

	if string(info.Labels[model.LabelName("hostname")]) == "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("host")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) == "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")]) + ":" + string(info.Labels[model.LabelName("host")])
	}
	graph, err := s.graphRepository.FindByID(ctx, history.GraphID)
	if graph.General != nil {
		format := graph.General.Yaxes.Format
		temMap["value"] = models.FormatValue(string(info.Annotations["value"]), format)
	} else {
		temMap["value"] = string(info.Annotations["value"])
	}
	owner := make([]string, 0)
	if graph.Alert != nil && graph.Alert.Receiver != nil && len(graph.Alert.Receiver.Dingdings) > 0 {
		token = graph.Alert.Receiver.Dingdings[0].Token
		for _, u := range graph.Alert.Receiver.Users {
			context2, _ := context.WithTimeout(ctx, 5*time2.Second)
			ctx2 := thrift.Wrap(context2)
			owner2, err := s.uic.GetUserByID(ctx2, u.ID)
			if err == nil {
				owner = append(owner, owner2.Phone)
			}
		}
	}
	temMap["name"] = graph.Name
	temMap["owners"] = owner
	temMap["message"] = info.Annotations["message"]
	if graph.Alert != nil {
		expression := graph.Alert.Expression + graph.Alert.Threshold
		if len(expression) > 200 {
			expression = expression[0:200] + "..."
		}
		temMap["expression"] = expression
		temMap["threshold"] = graph.Alert.Threshold
		if graph.Alert.AlertLevel == 0 {
			temMap["alert"] = "严重"
		} else if graph.Alert.AlertLevel == 1 {
			temMap["alert"] = "警告"
		} else {
			temMap["alert"] = "忽略"
		}
	}

	user := new(models.ApplicationOwner)
	err = json.Unmarshal([]byte(history.ClaimUser), &user)
	if err != nil {
		return err
	}
	temMap["claim"] = user.UserName

	if history.ApiID != 0 {
		api, err := s.apiRepository.FindByID(ctx, history.ApiID)
		if err != nil {
			return err
		}
		temMap["app"] = api.AppName
		if api.Type == 0 {
			temMap["api"] = api.HttpMethod + ":" + api.HttpPath
		} else {
			temMap["api"] = api.ZeusService + ":" + api.ZeusMethod
		}
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=interface"
		urlDetail += "&appId=" + strconv.Itoa(int(api.AppID))
		temMap["urlDetail"] = urlDetail
		if api.Type == 1 {
			appNames, _ := s.prome.QueryUpstream(api.ZeusMethod, api.ZeusService, time2.Now())
			if len(appNames) > 0 {
				upstreams := make([]*models.Application, 0)
				for _, app := range appNames {
					up, err := s.appRepository.FindByName(ctx, app)
					if err == nil {
						upApp := s.GetUsers(up)
						if upApp != nil {
							temMap["hasUp"] = "1"
							upstreams = append(upstreams, upApp)
							upsteamUsers = append(upsteamUsers, upApp.Users...)
						}
					}
				}
				upstreamsMap := make(map[string]*models.Application)
				for _, u := range upstreams {
					upstreamsMap[u.Name] = u
				}
				upstreams2 := make([]*models.Application, 0)
				for _, v := range upstreamsMap {
					upstreams2 = append(upstreams2, v)
				}
				temMap["upstreams"] = upstreams2
			}
		}
	} else if history.AppID != 0 {
		app, err := s.appRepository.FindByID(ctx, history.AppID)
		if err != nil {
			return err
		}
		temMap["app"] = app.Name
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=app"
		urlDetail += "&appId=" + strconv.Itoa(int(app.ID))
		temMap["urlDetail"] = urlDetail
	} else if history.DashboardID != 0 {
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=dashboard"
		temMap["urlDetail"] = urlDetail
	}

	s.logger.Debug("钉钉告警开始执行模版转化")
	err = t.Execute(&buf, temMap)
	if err != nil {
		s.logger.Error("execute template error", zap.Error(err))
		return err
	}
	msg := &dingding.DingMessage{
		MessageType: "markdown",
		Markdown: dingding.Markdown{
			Title: "认领",
			Text:  buf.String(),
		},
		At: dingding.At{
			IsAtAll: false,
		},
	}
	msg.At.AtMobiles = append(owner, upsteamUsers...)
	s.logger.Debug("钉钉告警开始发送")
	err = s.dingding.SendMessage(token, msg)
	if err != nil {
		s.logger.Error("钉钉告警失败发送", zap.Error(err))
	} else {
		s.logger.Debug("钉钉告警发送完成")
	}
	return err
}

func (s alertHistoryService) SendDingding2(ctx context.Context, history *models.AlertCurrent) error {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
		}
	}()
	var token string
	s.logger.Debug("钉钉告警开始获取graphID")
	t, err := template.New("temp").Parse(AlertClaimTmp)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	temMap := map[string]interface{}{
		"api":        "",
		"app":        "",
		"message":    "",
		"host":       "",
		"hasUp":      "",
		"alert":      "",
		"value":      "",
		"expression": "",
		"claim":      "",
		"upstreams":  "",
		"threshold":  "",
	}
	//查询dingding token
	s.logger.Debug("钉钉告警开始获取app")
	if len((*history.Alerts)) == 0 {
		return errors.New("don't exists alert message")
	}
	upsteamUsers := make([]string, 0)
	info := (*history.Alerts)[0]
	if string(info.Labels[model.LabelName("hostname")]) == "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("host")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) == "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")]) + ":" + string(info.Labels[model.LabelName("host")])
	}
	graph, err := s.graphRepository.FindByID(ctx, history.GraphID)
	if graph.General != nil {
		format := graph.General.Yaxes.Format
		temMap["value"] = models.FormatValue(string(info.Annotations["value"]), format)
	} else {
		temMap["value"] = string(info.Annotations["value"])
	}
	owner := make([]string, 0)
	if graph.Alert != nil && graph.Alert.Receiver != nil && len(graph.Alert.Receiver.Dingdings) > 0 {
		token = graph.Alert.Receiver.Dingdings[0].Token
		for _, u := range graph.Alert.Receiver.Users {
			context2, _ := context.WithTimeout(ctx, 5*time2.Second)
			ctx2 := thrift.Wrap(context2)
			owner2, err := s.uic.GetUserByID(ctx2, u.ID)
			if err == nil {
				owner = append(owner, owner2.Phone)
			}
		}
	}
	temMap["name"] = graph.Name
	temMap["owners"] = owner
	temMap["message"] = info.Annotations["message"]
	if graph.Alert != nil {
		expression := graph.Alert.Expression + graph.Alert.Threshold
		if len(expression) > 200 {
			expression = expression[0:200] + "..."
		}
		temMap["expression"] = expression
		temMap["threshold"] = graph.Alert.Threshold
		if graph.Alert.AlertLevel == 0 {
			temMap["alert"] = "严重"
		} else if graph.Alert.AlertLevel == 1 {
			temMap["alert"] = "警告"
		} else {
			temMap["alert"] = "忽略"
		}
	}

	user := new(models.ApplicationOwner)
	err = json.Unmarshal([]byte(history.ClaimUser), &user)
	if err != nil {
		return err
	}
	temMap["claim"] = user.UserName

	if history.ApiID != 0 {
		api, err := s.apiRepository.FindByID(ctx, history.ApiID)
		if err != nil {
			return err
		}
		temMap["app"] = api.AppName
		if api.Type == 0 {
			temMap["api"] = api.HttpMethod + ":" + api.HttpPath
		} else {
			temMap["api"] = api.ZeusService + ":" + api.ZeusMethod
		}
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=interface"
		urlDetail += "&appId=" + strconv.Itoa(int(api.AppID))
		temMap["urlDetail"] = urlDetail
		if api.Type == 1 {
			appNames, _ := s.prome.QueryUpstream(api.ZeusMethod, api.ZeusService, time2.Now())
			if len(appNames) > 0 {
				upstreams := make([]*models.Application, 0)
				for _, app := range appNames {
					up, err := s.appRepository.FindByName(ctx, app)
					if err == nil {
						upApp := s.GetUsers(up)
						if upApp != nil {
							temMap["hasUp"] = "1"
							upstreams = append(upstreams, upApp)
							upsteamUsers = append(upsteamUsers, upApp.Users...)
						}
					}
				}
				upstreamsMap := make(map[string]*models.Application)
				for _, u := range upstreams {
					upstreamsMap[u.Name] = u
				}
				upstreams2 := make([]*models.Application, 0)
				for _, v := range upstreamsMap {
					upstreams2 = append(upstreams2, v)
				}
				temMap["upstreams"] = upstreams2
			}
		}
	} else if history.AppID != 0 {
		app, err := s.appRepository.FindByID(ctx, history.AppID)
		if err != nil {
			return err
		}
		temMap["app"] = app.Name
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=app"
		urlDetail += "&appId=" + strconv.Itoa(int(app.ID))
		temMap["urlDetail"] = urlDetail
	}
	s.logger.Debug("钉钉告警开始执行模版转化")
	err = t.Execute(&buf, temMap)
	if err != nil {
		s.logger.Error("execute template error", zap.Error(err))
		return err
	}
	msg := &dingding.DingMessage{
		MessageType: "markdown",
		Markdown: dingding.Markdown{
			Title: "认领",
			Text:  buf.String(),
		},
		At: dingding.At{
			IsAtAll: false,
		},
	}
	msg.At.AtMobiles = append(owner, upsteamUsers...)
	s.logger.Debug("钉钉告警开始发送")
	err = s.dingding.SendMessage(token, msg)
	if err != nil {
		s.logger.Error("钉钉告警失败发送", zap.Error(err))
	} else {
		s.logger.Debug("钉钉告警发送完成")
	}
	return err
}

func (s alertHistoryService) GetUsers(app *models.Application) *models.Application {
	owner := app.GetOwners()
	for _, user := range owner {
		ctx, _ := tchannel.NewContextBuilder(time2.Second).Build()
		owner, err := s.uic.GetUserByID(ctx, user.UserID)
		if err == nil {
			app.Users = append(app.Users, owner.Phone)
		}
	}
	return app
}
